"""SciPy库的optimize模块主要用于执行各种优化任务。
优化是寻找特定函数的最小值或最大值的过程，通常用于机器学习、数据分析、工程和其他领域。

scipy.optimize提供了多种优化算法，包括梯度下降法、牛顿法、最小二乘法等，可以解决各种复杂的优化问题。
该模块还包含一些特定的函数，用于解决某些特定类型的优化问题，如多维非线性优化、约束优化、最小二乘问题等。
此外，scipy.optimize还提供了一些工具，如多线程支持、边界条件处理、数值稳定性措施等，以提高优化的效率和准确性。

1.
主要功能
最优化是数学学科中的一个重要研究领域，optimize模块包含的各类函数能够帮助我们节省大量的计算时间和精力。

类别                          说明
优化                       包含标量函数优化，局部优化，全局优化等各类方法
最小二乘法和曲线拟合           包含求解最小二乘法问题，各种拟合曲线的方法
求根                        包含多种求根的方法，比如布伦特方法，牛顿 - 拉夫森方法等10来种求根方法
线性规划                     内置多种线性规划算法以及混合整数线性规划计算等
分配问题                      解决线性和分配问题，包括二次分配和图匹配问题的近似解等
工具函数                     包含一些通用的计算方法，比如有限差分近似，海森近似，线搜索等计算函数
遗留函数                    即将被淘汰的一些函数，不建议再继续使用
下面通过曲线拟合和非线性方程组求解两个示例演示optimize模块的使用。

2.
曲线拟合示例
所谓曲线拟合，其实就是找到一个函数，能够尽可能的经过或接近一系列离散的点。
然后就可以用这个函数来预测离散点的变化趋势。

2.1.最小二乘法
optimize模块的最小二乘法拟合曲线需要定义一个目标函数和一个残差函数。
最小二乘法通过迭代寻找目标函数中参数的最优值，
而残差函数是用来计算目标函数的返回值和实际值之间的误差的。

首先，加载需要拟合的离散数据。
"""
import numpy as np
import pandas as pd
from matplotlib.ticker import MultipleLocator
import matplotlib.pyplot as plt
from scipy.optimize import least_squares
from scipy.optimize import curve_fit
from scipy.optimize import root
import matplotlib
import matplot_config

matplot_config.init_config()

data = pd.read_csv("./datas/A0A-人民生活/A0A01.csv")
data = data[data["zb"] == "A0A0101"]
data = data.sort_values("sj")
data.head()
"""image.png
数据来源：https: // databook.top / nation / A0A （其中的A0A01.csv）

然后，依据其中1978年
~2022
年的居民人均可支配收入绘制散点图。"""

ax = plt.subplot()
ax.scatter(data["sjCN"], data["value"], marker='*', color='r')
ax.xaxis.set_major_locator(MultipleLocator(4))
ax.set_title("居民人均可支配收入(元)")

plt.xticks(rotation=45)
plt.show()


# image.png
# 
# 最后，用optimize模块提供的最小二乘法拟合居民人均可支配收入的变化曲线。


# 目标函数
def target_func(p, x):
    return p[0] * np.exp(p[1] * x) + p[2]


# 残差函数
def residual(p, x, dy):
    return target_func(p, x) - dy


p0 = [1, 1, 0]
x = range(len(data))
y = data["value"]
# 最小二乘法迭代目标函数的参数
result = least_squares(residual, p0, args=(x, y))

ax = plt.subplot()
ax.xaxis.set_major_locator(MultipleLocator(4))
ax.set_title("居民人均可支配收入(元)")

ax.scatter(data["sjCN"], data["value"], marker='*', color='r')
# 这里的result.x就是迭代后的最优参数
ax.plot(x, target_func(result.x, x), color='g')

plt.xticks(rotation=45)
plt.show()
"""image.png
图中绿色的曲线就是拟合的曲线，根据拟合出的曲线和目标函数，
就可以预测以后的居民人均可支配收入的变化情况。

2.2.curve_fit方法
最小二乘法需要定义目标函数和残差函数，使用起来有些繁琐，optimize模块中还提供了一个curve_fit函数。
可以简化曲线拟合的过程。
"""


# 目标函数
def curve_fit_func(x, p0, p1, p2):
    return p0 * np.exp(p1 * x) + p2


# fitp 就是计算出的目标函数的最优参数
fitp, _ = curve_fit(curve_fit_func, x, y, [1, 1, 0])

ax = plt.subplot()
ax.xaxis.set_major_locator(MultipleLocator(4))
ax.set_title("居民人均可支配收入(元)")

ax.scatter(data["sjCN"], data["value"], marker='*', color='r')
ax.plot(x, curve_fit_func(x, *fitp), color='b')

plt.xticks(rotation=45)
plt.show()
"""image.png
蓝色的线就是拟合曲线，拟合结果和使用最小二乘法拟合出的是一样的，只是代码可以简化一些。

3.
非线性方程组求解示例
众所周知，手工求解非线性方程是非常困难的，如果经常遇到求解非线性方程的情况，optimize模块绝对能成为你的一个称手工具。

3.1.非线性方程
使用optimize模块求解非线性方程非常简单。
比如方程：
2x+sin(x)−x3=0
"""

f = lambda x: 2 ** x + np.sin(x) - x ** 3

result = root(f, [1, 1], method='hybr')

"""# result.x 是方程的解
result.x
# 运行结果：
array([1.58829918, 1.58829918])
实际使用时，将变量f对应的方程换成你的方程即可。
注意，求解方程的
root
方法的参数method，这个参数支持多种求解方程的方法，可以根据方程的特点选择不同的method。

支持的method列表可参考官方文档：https: // docs.scipy.org / doc / scipy / reference / optimize.html  # multidimensional

3.2.非线性方程组
对于方程组，求解的方法如下：
比如方程组：
{    x2+y−3=0    (x−2)2+y−1=0
"""
fs = lambda x: np.array(
    [
        x[0] ** 2 + x[1] - 3,
        (x[0] - 2) ** 2 + x[1] - 1,
    ]
)

result = root(fs, [1, 1], method="hybr")
result.x
"""# 运行结果：
array([1.5, 0.75])
方程组中方程个数多的话，直接添加到变量fs的数组中即可。

4.
总结
总的来说，scipy.optimize是一个强大且易用的优化工具箱，用于解决各种复杂的优化问题。
它对于需要优化算法的许多科学和工程领域都具有重要价值。
通过使用这个模块，用户可以节省大量时间和精力，同时还能保证优化的质量和准确性。"""
